home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Developer Toolbox 6.1
/
SGI Developer Toolbox 6.1 - Disc 4.iso
/
public
/
plan
/
src
/
usermenu.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-01
|
18KB
|
621 lines
/*
* Create and destroy user menus and all widgets in them. All widgets
* are labels or pushbuttons; they are faster than text buttons. Whenever
* the user presses a button with text in it, it is overlaid with a Text
* button. For editing and input into the Text button, see useredit.c.
*
* destroy_user_popup() remove user popup
* create_user_popup() create user popup
* force_user_list_update() make update_user_lists() unconditional
* update_user_lists() for each user in the list, re-read the
* user's public appts into user[].list
* if necessary
*/
#include <stdio.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef MIPS
#include <stdlib.h>
#endif
#include <pwd.h>
#include <Xm/Xm.h>
#include <Xm/Form.h>
#include <Xm/LabelP.h>
#include <Xm/LabelG.h>
#include <Xm/PushBP.h>
#include <Xm/PushBG.h>
#include <Xm/ToggleB.h>
#include <Xm/ScrolledW.h>
#include <Xm/Text.h>
#include <Xm/Protocols.h>
#include "cal.h"
#define NCOLUMNS 4 /* # of widget columns in user list */
extern char *mystrdup();
extern void help_callback();
static void create_user_rows(), edit_user_button(), got_user_text(),
draw_row(), delete_callback(), sort_callback(),
done_callback(), list_callback(), got_text();
#ifdef MIPS
extern struct passwd *getpwnam();
#endif
extern Display *display; /* everybody uses the same server */
extern GC gc; /* everybody uses this context */
extern Pixel color[NCOLS]; /* colors: COL_* */
extern Pixmap pixmap[NPICS]; /* common symbols */
extern struct config config; /* global configuration data */
extern struct list *mainlist; /* list of all schedule entries */
extern struct mainmenu mainmenu; /* all important main window widgets */
extern struct user *user; /* user list (from file_r.c) */
extern int nusers; /* number of users in user list */
static BOOL have_shell; /* message popup exists if TRUE */
static Widget shell; /* popup menu shell */
static Widget delete; /* delete button, for desensitizing */
static Widget info; /* info line for error messages */
static Widget textwidget; /* if editing, text widget; else 0 */
static int have_nrows; /* # of table widget rows allocated */
static int xedit, yedit; /* if editing, column/row */
static int ycurr; /* current row, 0=none, 1=1st user...*/
static Widget ulist; /* user list RowColumn widget */
static Widget (*utable)[4]; /* all widgets in user list table */
/* [0][*] is title row */
/*
* destroy the popup. Remove it from the screen, and destroy its widgets.
* Redraw the week menu if there is one.
*/
destroy_user_popup()
{
if (have_shell) {
XtPopdown(shell);
XTDESTROYWIDGET(shell);
have_shell = FALSE;
mainlist->modified = TRUE;
draw_week_calendar();
}
}
/*
* create a user popup as a separate application shell.
*/
create_user_popup()
{
Widget form, scroll, w;
Arg args[15];
int n;
Atom closewindow;
if (have_shell)
return;
n = 0;
XtSetArg(args[n], XmNdeleteResponse, XmUNMAP); n++;
XtSetArg(args[n], XmNiconic, False); n++;
shell = XtAppCreateShell("User List", "plan",
applicationShellWidgetClass, display, args, n);
# ifdef EDITRES
XtAddEventHandler(shell, (EventMask)0, TRUE,
_XEditResCheckMessages, NULL);
# endif
set_icon(shell, 1);
form = XtCreateWidget("userform", xmFormWidgetClass,
shell, NULL, 0);
XtAddCallback(form, XmNhelpCallback, help_callback, (XtPointer)"user");
/*-- buttons --*/
n = 0;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNbottomOffset, 8); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNleftOffset, 8); n++;
XtSetArg(args[n], XmNwidth, 80); n++;
XtSetArg(args[n], XmNsensitive, False); n++;
delete = w = XtCreateManagedWidget("Delete", xmPushButtonWidgetClass,
form, args, n);
XtAddCallback(w, XmNactivateCallback, delete_callback, (XtPointer)0);
XtAddCallback(w, XmNhelpCallback, help_callback, (XtPointer)
"user_delete");
n = 0;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNbottomOffset, 8); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNleftWidget, w); n++;
XtSetArg(args[n], XmNleftOffset, 8); n++;
XtSetArg(args[n], XmNwidth, 80); n++;
w = XtCreateManagedWidget("Sort", xmPushButtonWidgetClass,
form, args, n);
XtAddCallback(w, XmNactivateCallback, sort_callback, (XtPointer)0);
XtAddCallback(w, XmNhelpCallback, help_callback, (XtPointer)
"user_sort");
n = 0;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNbottomOffset, 8); n++;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNrightOffset, 8); n++;
XtSetArg(args[n], XmNwidth, 80); n++;
w = XtCreateManagedWidget("Done", xmPushButtonWidgetClass,
form, args, n);
XtAddCallback(w, XmNactivateCallback, done_callback, (XtPointer)0);
XtAddCallback(w, XmNhelpCallback, help_callback, (XtPointer)
"user_done");
n = 0;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNbottomOffset, 8); n++;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNrightWidget, w); n++;
XtSetArg(args[n], XmNrightOffset, 8); n++;
XtSetArg(args[n], XmNwidth, 80); n++;
w = XtCreateManagedWidget("Help", xmPushButtonWidgetClass,
form, args, n);
XtAddCallback(w, XmNactivateCallback, help_callback, (XtPointer)
"user");
XtAddCallback(w, XmNhelpCallback, help_callback, (XtPointer)
"user");
/*-- infotext -- */
n = 0;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNleftOffset, 8); n++;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNrightOffset, 8); n++;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNbottomWidget, w); n++;
XtSetArg(args[n], XmNbottomOffset, 8); n++;
XtSetArg(args[n], XmNalignment, XmALIGNMENT_BEGINNING); n++;
info = XtCreateManagedWidget(" ", xmLabelGadgetClass,
form, args, n);
/*-- scroll --*/
n = 0;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNtopOffset, 8); n++;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNbottomWidget, info); n++;
XtSetArg(args[n], XmNbottomOffset, 8); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNleftOffset, 8); n++;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNrightOffset, 8); n++;
XtSetArg(args[n], XmNwidth, 580); n++;
XtSetArg(args[n], XmNheight, 300); n++;
XtSetArg(args[n], XmNscrollingPolicy, XmAUTOMATIC); n++;
scroll = XtCreateWidget("uscroll", xmScrolledWindowWidgetClass,
form, args, n);
XtAddCallback(scroll, XmNhelpCallback, help_callback, (XtPointer)
"user");
n = 0;
ulist = XtCreateManagedWidget("ulist", xmBulletinBoardWidgetClass,
scroll, args, n);
create_user_rows(); /* have_shell must be FALSE here */
XtManageChild(scroll);
XtManageChild(form);
XtPopup(shell, XtGrabNone);
closewindow = XmInternAtom(display, "WM_DELETE_WINDOW", False);
XmAddWMProtocolCallback(shell, closewindow,
done_callback, (XtPointer)shell);
have_shell = TRUE;
}
/*
* makes sure there are enough widget rows for schedule entries. Also makes
* sure that there aren't too many, for speed reasons. Allocate one extra
* widget row for the title at the top. All the text buttons are
* label widgets. For performance reasons, they are overlaid by a text
* widget when pressed.
* No text is printed into the buttons yet, this is done later by draw_users().
*/
static short cell_x [NCOLUMNS] = { 4, 54, 114, 234 };
static short cell_xs [NCOLUMNS] = { 30, 60, 120, 300 };
static char *cell_name [NCOLUMNS] = { " ", "Group", "User", "Home" };
static char *cell_help [NCOLUMNS] = { "user_enable", "user_color",
"user_name", "user_home" };
static void create_user_rows()
{
int nrows = nusers+5 - nusers%5;
int x, y;
Arg args[15];
int n;
int align;
char *name;
WidgetClass class;
if (!have_shell) /* check # of rows: */
have_nrows = 0;
if (nrows <= have_nrows)
return;
n = (nrows+1) * NCOLUMNS * sizeof(Widget *);
if (utable && !(utable = (Widget (*)[])realloc(utable, n)) ||
!utable && !(utable = (Widget (*)[])malloc(n)))
fatal("no memory");
for (x=0; x < NCOLUMNS; x++) {
for (y=have_nrows; y <= nrows; y++) {
align = XmALIGNMENT_BEGINNING;
XtUnmanageChild(ulist);
name = cell_name[x];
class = xmPushButtonWidgetClass;
n = 0;
if (y) {
if (x == 0) {
class = xmToggleButtonWidgetClass;
XtSetArg(args[n], XmNselectColor,
color[COL_TOGGLE]); n++;
}
name = " ";
} else {
class = xmLabelWidgetClass;
align = XmALIGNMENT_CENTER;
}
XtSetArg(args[n], XmNx, cell_x[x]); n++;
XtSetArg(args[n], XmNy, 10 + 30*y); n++;
XtSetArg(args[n], XmNwidth, cell_xs[x]); n++;
XtSetArg(args[n], XmNheight, 30); n++;
XtSetArg(args[n], XmNalignment, align); n++;
XtSetArg(args[n], XmNrecomputeSize, False); n++;
XtSetArg(args[n], XmNtraversalOn, True); n++;
XtSetArg(args[n], XmNhighlightThickness,0); n++;
XtSetArg(args[n], XmNshadowThickness, x && y); n++;
utable[y][x] = XtCreateManagedWidget(name, class,
ulist, args, n);
if (y)
XtAddCallback(utable[y][x],
x ? XmNactivateCallback
: XmNvalueChangedCallback,
list_callback, (XtPointer)(x + y * NCOLUMNS));
XtAddCallback(utable[y][x], XmNhelpCallback, help_callback,
(XtPointer)cell_help[x]);
}
}
for (y=have_nrows; y <= nrows; y++)
draw_row(y);
have_nrows = nrows;
XtManageChild(ulist);
}
/*-------------------------------------------------- editing ----------------*/
/*
* turn a text label into a Text button, to allow user input. This is done
* by simply installing a text widget on top of the label widget. The proper
* user name or home dir is put into the text widget. The previously edited
* button is un-edited.
*/
static void edit_user_button(doedit, x, y)
BOOL doedit; /* TRUE=edit, FALSE=unedit */
int x; /* column, 0..NCOLUMNS-1* */
int y; /* row, y=0: title */
{
Arg args[15];
int n;
char *text;
if (textwidget) {
char *string = XmTextGetString(textwidget);
got_user_text(xedit, yedit, string);
XtFree(string);
XtDestroyWidget(textwidget);
if (yedit && yedit <= nusers)
user[yedit-1].suspended = 0;
draw_row(yedit);
create_user_rows();
}
textwidget = 0;
if (!doedit)
return;
if (y > nusers+1)
y = nusers+1;
n = 0;
XtSetArg(args[n], XmNx, cell_x[x]); n++;
XtSetArg(args[n], XmNy, 10 + 30*y); n++;
XtSetArg(args[n], XmNwidth, cell_xs[x]); n++;
XtSetArg(args[n], XmNheight, 30); n++;
XtSetArg(args[n], XmNrecomputeSize, False); n++;
XtSetArg(args[n], XmNpendingDelete, True); n++;
XtSetArg(args[n], XmNhighlightThickness,0); n++;
XtSetArg(args[n], XmNshadowThickness, 1); n++;
XtSetArg(args[n], XmNbackground, color[COL_TEXTBACK]); n++;
textwidget = XtCreateManagedWidget("text", xmTextWidgetClass,
ulist, args, n);
XtAddCallback(textwidget, XmNactivateCallback, got_text, (XtPointer)0);
XtAddCallback(textwidget, XmNhelpCallback, help_callback,
(XtPointer)cell_help[x]);
XmProcessTraversal(textwidget, XmTRAVERSE_CURRENT);
text = y > nusers ? "" : x == 2 ? user[y-1].name : user[y-1].home;
print_text_button(textwidget, "%s", text);
xedit = x;
yedit = y;
}
static void got_user_text(x, y, string)
int x; /* column, 0..NCOLUMNS-1* */
int y; /* row, y=0: title */
char *string; /* text entered by user */
{
struct passwd *pw; /* for searching home dirs */
register struct user *u;
char buf[100];
int i;
if (!y--)
return;
while ((i = strlen(string)) && string[i-1] == ' ')
string[i-1] = 0;
if (!*string)
return;
if (y >= nusers) {
int n = ++nusers * sizeof(struct user);
if (user && !(user = (struct user *)realloc(user, n)) ||
!user && !(user = (struct user *)malloc(n)))
fatal("no memory");
y = nusers-1;
u = user+y;
u->name = 0;
u->home = 0;
u->suspended = 0;
u->color = 0;
u->time = 0;
u->list = 0;
}
u = user+y;
if (x == 2) { /* name */
if (u->name)
free(u->name);
if (u->home)
free(u->home);
u->name = mystrdup(string);
u->home = 0;
} else { /* home */
if (u->home)
free(u->home);
u->home = mystrdup(string);
if (*u->home && access(u->home, 1))
print_button(info, "%s: cannot access", u->home);
}
if (!*string)
print_button(info, "Null user not allowed");
if (u->name && *u->name && !u->home) { /* default home? */
if (pw = getpwnam(string))
u->home = mystrdup(pw->pw_dir);
else {
sprintf(buf, "~%s", string);
print_button(info, "%s: no such user", string);
u->home = mystrdup(buf);
}
}
}
/*
* draw all buttons of row y. y must be > 0 because 0 is the title row.
* If y is > nusers, the row is blanked.
*/
static void draw_row(y)
int y;
{
Arg arg;
register struct user *u = &user[y-1];
if (y < 1)
return;
if (y <= nusers) { /* valid row */
XtSetArg(arg, XmNset, !u->suspended);
XtSetValues (utable[y][0], &arg, 1);
XtSetArg(arg, XmNbackground, color[COL_WUSER_0 + u->color]);
XtSetValues (utable[y][1], &arg, 1);
print_button(utable[y][2], u->name);
print_button(utable[y][3], u->home);
} else { /* blank row */
XtSetArg(arg, XmNset, 0);
XtSetValues (utable[y][0], &arg, 1);
XtSetArg(arg, XmNbackground, color[COL_BACK]);
XtSetValues (utable[y][1], &arg, 1);
print_button(utable[y][2], " ");
print_button(utable[y][3], " ");
}
}
/*-------------------------------------------------- read lists -------------*/
/*
* make sure that all user lists are up-to-date. Don't check more than once
* every 10 seconds though, to prevent lots of lengthy stat() calls.
*/
static time_t last_test; /* last time we stat'ed all files */
force_user_list_update()
{
last_test = 0;
}
update_user_lists()
{
BOOL reread; /* need to re-stat files? */
register struct user *u; /* current user slot */
int n; /* # of current user slot */
struct stat file; /* for reading file mod time */
char path[1024]; /* user's .dayplan file name */
reread = get_time() > last_test + 10;
for (u=user, n=0; n < nusers; n++, u++) {
if (u->suspended || !reread && u->list || !u->home)
continue;
sprintf(path, "%s/%s", u->home, DB_FILE);
if (u->list) {
if (stat(path, &file)) {
perror(path);
continue;
}
if (last_test && u->time < file.st_mtime)
continue;
free((char *)u->list);
u->list = 0;
}
if (!readfile(&u->list, path, FALSE))
perror(path);
}
last_test = get_time();
}
/*-------------------------------------------------- callbacks --------------*/
/*
* Delete, Add-all, Sort, and Done buttons
* All of these routines are direct X callbacks.
*/
/*ARGSUSED*/
static void delete_callback(widget, item, data)
Widget widget;
int item;
XmToggleButtonCallbackStruct *data;
{
int n;
Arg args;
if (ycurr && ycurr <= nusers) {
edit_user_button(FALSE, 0, 0);
for (n=ycurr-1; n < nusers; n++)
user[n] = user[n+1];
n = --nusers * sizeof(struct user);
if (n && !(user = (struct user *)realloc(user, n)))
fatal("no memory");
for (n=1; n <= have_nrows; n++)
draw_row(n);
}
if (!ycurr) {
XtSetArg(args, XmNsensitive, 0);
XtSetValues(delete, &args, 1);
}
}
/* aren't prototypes annoying? :-) */
#if defined(ULTRIX) || defined(MIPS)
/* this means Ultrix 4.2A. If Ultrix 4.3 complains about */
/* a missing const, change the following definition. */
#define CONST
#else
#define CONST const
#endif
static int compare(u, v) register CONST void *u, *v; {
return( ((struct user *)u)->color == ((struct user *)v)->color
? strcmp(((struct user *)u)->name, ((struct user *)v)->name)
: ((struct user *)u)->color - ((struct user *)v)->color); }
/*ARGSUSED*/
static void sort_callback(widget, item, data)
Widget widget;
int item;
XmToggleButtonCallbackStruct *data;
{
Arg args;
int n;
if (nusers) {
edit_user_button(FALSE, 0, 0);
XtSetArg(args, XmNsensitive, 0);
XtSetValues(delete, &args, 1);
qsort(user, nusers, sizeof(struct user), compare);
for (n=1; n <= nusers; n++)
draw_row(n);
}
}
/*ARGSUSED*/
static void done_callback(widget, item, data)
Widget widget;
int item;
XmToggleButtonCallbackStruct *data;
{
edit_user_button(FALSE, 0, 0);
destroy_user_popup();
}
/*
* one of the buttons in the list was pressed
*/
/*ARGSUSED*/
static void list_callback(widget, item, data)
Widget widget;
int item;
XmToggleButtonCallbackStruct *data;
{
int x = item % NCOLUMNS;
int y = item / NCOLUMNS;
Arg arg;
if (y > nusers) { /* new entry */
if (x != 2) {
print_button(info, "Enter user name first");
return;
}
ycurr = 0;
edit_user_button(TRUE, x, nusers+1);
} else { /* old entry */
ycurr = y;
switch(x) {
case 0: /* on/off */
user[y-1].suspended = !data->set;
break;
case 1: /* color */
user[y-1].color++;
user[y-1].color &= 7;
XtSetArg(arg, XmNbackground,
color[COL_WUSER_0 + user[y-1].color]);
XtSetValues(widget, &arg, 1);
break;
case 2: /* username */
case 3: /* home dir */
edit_user_button(TRUE, x, y);
}
}
XtSetArg(arg, XmNsensitive, ycurr > 0);
XtSetValues(delete, &arg, 1);
print_button(info, " ");
}
/*
* the user pressed Return in a text entry button
*/
/*ARGSUSED*/
static void got_text(widget, item, data)
Widget widget;
int item;
XmToggleButtonCallbackStruct *data;
{
edit_user_button(FALSE, 0, 0);
}